#include "des3.h"
#include <string.h>

/* Key schedule-related tables from FIPS-46 */

/* permuted choice table (key) */
static bits8_t pc1[] =
{
    57, 49, 41, 33, 25, 17,  9,
    1, 58, 50, 42, 34, 26, 18,
    10,  2, 59, 51, 43, 35, 27,
    19, 11,  3, 60, 52, 44, 36,

    63, 55, 47, 39, 31, 23, 15,
    7, 62, 54, 46, 38, 30, 22,
    14,  6, 61, 53, 45, 37, 29,
    21, 13,  5, 28, 20, 12,  4
};

/* number left rotations of pc1 */
static bits8_t totrot[] =
{
    1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
};

/* permuted choice key (table) */
static bits8_t pc2[] =
{
    14, 17, 11, 24,  1,  5,
    3, 28, 15,  6, 21, 10,
    23, 19, 12,  4, 26,  8,
    16,  7, 27, 20, 13,  2,
    41, 52, 31, 37, 47, 55,
    30, 40, 51, 45, 33, 48,
    44, 49, 39, 56, 34, 53,
    46, 42, 50, 36, 29, 32
};

/* End of DES-defined tables */

/* bit 0 is left-most in byte */
static sbits16_t bytebit[] =
{
    0200,0100,040,020,010,04,02,01
};


static void des3key  (DES3_KS ks, bits8_t *key, int decrypt);
static void des3     (DES3_KS ks, bits8_t *block, bits8_t *out);

static void des3ecb_block_encrypt (DES3_CTX *ctx, bits8_t *in, bits8_t *out);
static void des3cbc_block_decrypt (DES3_CTX *ctx, bits8_t *in, bits8_t *out);
static void des3cbc_block_encrypt (DES3_CTX *ctx, bits8_t *in, bits8_t *out);

/* Generate key schedule for encryption or decryption
 * depending on the value of "decrypt"
 */

/* 0 = encrypt, 1 = decrypt */
void deskey (DES_KS k, bits8_t *key, int decrypt)
{
    bits8_t pc1m[56];		/* place to modify pc1 into */
    bits8_t pcr[56];		/* place to rotate pc1 into */
    register int i,j,l;
    int m;
    bits8_t ks[8];

    for (j=0; j<56; j++)  		/* convert pc1 to bits of key */
    {
        l=pc1[j]-1;		/* integer bit location	 */
        m = l & 07;		/* find bit		 */
        pc1m[j]=(key[l>>3] &	/* find which key byte l is in */
                 bytebit[m])	/* and which bit of that byte */
                ? 1 : 0;	/* and store 1-bit result */
    }
    for (i=0; i<16; i++)  		/* key chunk for each iteration */
    {
        memset(ks, 0, sizeof(ks));	/* Clear key schedule */
        for (j=0; j<56; j++)	/* rotate pc1 the right amount */
            pcr[j] = pc1m[(l=j+totrot[decrypt? 15-i : i])<(j<28? 28 : 56) ? l: l-28];
        /* rotate left and right halves independently */
        for (j=0; j<48; j++) 	/* select bits individually */
        {
            /* check bit that goes to ks[j] */
            if (pcr[pc2[j]-1])
            {
                /* mask it in if it's there */
                l= j % 6;
                ks[j/6] |= bytebit[l] >> 2;
            }
        }
        /* Now convert to packed odd/even interleaved form */
        k[i][0] = ((sbits32_t)ks[0] << 24)
                  | ((sbits32_t)ks[2] << 16)
                  | ((sbits32_t)ks[4] << 8)
                  | ((sbits32_t)ks[6]);
        k[i][1] = ((sbits32_t)ks[1] << 24)
                  | ((sbits32_t)ks[3] << 16)
                  | ((sbits32_t)ks[5] << 8)
                  | ((sbits32_t)ks[7]);
    }
}

/* Generate key schedule for triple DES in E-D-E (or D-E-D) mode.
 *
 * The key argument is taken to be 24 bytes. The first 8 bytes are K1
 * for the first stage, the second 8 bytes are K2 for the middle stage
 * and the third 8 bytes are K3 for the last stage.
 *
 *    DES3_KS k         key schedule to be initialized
 *    bits8_t *key	192 bits (24 bytes) of key (will use only 168)
 *    int decrypt 	0 = encrypt, 1 = decrypt
 */
void des3key (DES3_KS k, bits8_t *key, int decrypt)
{
    if (!decrypt)
    {
        deskey(&k[0],&key[0],0);
        deskey(&k[16],&key[8],1);
        deskey(&k[32],&key[16],0);
    }
    else
    {
        deskey(&k[32],&key[0],1);
        deskey(&k[16],&key[8],0);
        deskey(&k[0],&key[16],1);
    }
}

/* Combined SP lookup table, linked in
 * For best results, ensure that this is aligned on a 32-bit boundary;
 * Borland C++ 3.1 doesn't guarantee this!
 */
const static bits32_t Spbox[8][64] =
{
    {
        0x01010400L,0x00000000L,0x00010000L,0x01010404L,
        0x01010004L,0x00010404L,0x00000004L,0x00010000L,
        0x00000400L,0x01010400L,0x01010404L,0x00000400L,
        0x01000404L,0x01010004L,0x01000000L,0x00000004L,
        0x00000404L,0x01000400L,0x01000400L,0x00010400L,
        0x00010400L,0x01010000L,0x01010000L,0x01000404L,
        0x00010004L,0x01000004L,0x01000004L,0x00010004L,
        0x00000000L,0x00000404L,0x00010404L,0x01000000L,
        0x00010000L,0x01010404L,0x00000004L,0x01010000L,
        0x01010400L,0x01000000L,0x01000000L,0x00000400L,
        0x01010004L,0x00010000L,0x00010400L,0x01000004L,
        0x00000400L,0x00000004L,0x01000404L,0x00010404L,
        0x01010404L,0x00010004L,0x01010000L,0x01000404L,
        0x01000004L,0x00000404L,0x00010404L,0x01010400L,
        0x00000404L,0x01000400L,0x01000400L,0x00000000L,
        0x00010004L,0x00010400L,0x00000000L,0x01010004L
    },

    {
        0x80108020L,0x80008000L,0x00008000L,0x00108020L,
        0x00100000L,0x00000020L,0x80100020L,0x80008020L,
        0x80000020L,0x80108020L,0x80108000L,0x80000000L,
        0x80008000L,0x00100000L,0x00000020L,0x80100020L,
        0x00108000L,0x00100020L,0x80008020L,0x00000000L,
        0x80000000L,0x00008000L,0x00108020L,0x80100000L,
        0x00100020L,0x80000020L,0x00000000L,0x00108000L,
        0x00008020L,0x80108000L,0x80100000L,0x00008020L,
        0x00000000L,0x00108020L,0x80100020L,0x00100000L,
        0x80008020L,0x80100000L,0x80108000L,0x00008000L,
        0x80100000L,0x80008000L,0x00000020L,0x80108020L,
        0x00108020L,0x00000020L,0x00008000L,0x80000000L,
        0x00008020L,0x80108000L,0x00100000L,0x80000020L,
        0x00100020L,0x80008020L,0x80000020L,0x00100020L,
        0x00108000L,0x00000000L,0x80008000L,0x00008020L,
        0x80000000L,0x80100020L,0x80108020L,0x00108000L
    },
    {
        0x00000208L,0x08020200L,0x00000000L,0x08020008L,
        0x08000200L,0x00000000L,0x00020208L,0x08000200L,
        0x00020008L,0x08000008L,0x08000008L,0x00020000L,
        0x08020208L,0x00020008L,0x08020000L,0x00000208L,
        0x08000000L,0x00000008L,0x08020200L,0x00000200L,
        0x00020200L,0x08020000L,0x08020008L,0x00020208L,
        0x08000208L,0x00020200L,0x00020000L,0x08000208L,
        0x00000008L,0x08020208L,0x00000200L,0x08000000L,
        0x08020200L,0x08000000L,0x00020008L,0x00000208L,
        0x00020000L,0x08020200L,0x08000200L,0x00000000L,
        0x00000200L,0x00020008L,0x08020208L,0x08000200L,
        0x08000008L,0x00000200L,0x00000000L,0x08020008L,
        0x08000208L,0x00020000L,0x08000000L,0x08020208L,
        0x00000008L,0x00020208L,0x00020200L,0x08000008L,
        0x08020000L,0x08000208L,0x00000208L,0x08020000L,
        0x00020208L,0x00000008L,0x08020008L,0x00020200L
    },
    {
        0x00802001L,0x00002081L,0x00002081L,0x00000080L,
        0x00802080L,0x00800081L,0x00800001L,0x00002001L,
        0x00000000L,0x00802000L,0x00802000L,0x00802081L,
        0x00000081L,0x00000000L,0x00800080L,0x00800001L,
        0x00000001L,0x00002000L,0x00800000L,0x00802001L,
        0x00000080L,0x00800000L,0x00002001L,0x00002080L,
        0x00800081L,0x00000001L,0x00002080L,0x00800080L,
        0x00002000L,0x00802080L,0x00802081L,0x00000081L,
        0x00800080L,0x00800001L,0x00802000L,0x00802081L,
        0x00000081L,0x00000000L,0x00000000L,0x00802000L,
        0x00002080L,0x00800080L,0x00800081L,0x00000001L,
        0x00802001L,0x00002081L,0x00002081L,0x00000080L,
        0x00802081L,0x00000081L,0x00000001L,0x00002000L,
        0x00800001L,0x00002001L,0x00802080L,0x00800081L,
        0x00002001L,0x00002080L,0x00800000L,0x00802001L,
        0x00000080L,0x00800000L,0x00002000L,0x00802080L
    },
    {
        0x00000100L,0x02080100L,0x02080000L,0x42000100L,
        0x00080000L,0x00000100L,0x40000000L,0x02080000L,
        0x40080100L,0x00080000L,0x02000100L,0x40080100L,
        0x42000100L,0x42080000L,0x00080100L,0x40000000L,
        0x02000000L,0x40080000L,0x40080000L,0x00000000L,
        0x40000100L,0x42080100L,0x42080100L,0x02000100L,
        0x42080000L,0x40000100L,0x00000000L,0x42000000L,
        0x02080100L,0x02000000L,0x42000000L,0x00080100L,
        0x00080000L,0x42000100L,0x00000100L,0x02000000L,
        0x40000000L,0x02080000L,0x42000100L,0x40080100L,
        0x02000100L,0x40000000L,0x42080000L,0x02080100L,
        0x40080100L,0x00000100L,0x02000000L,0x42080000L,
        0x42080100L,0x00080100L,0x42000000L,0x42080100L,
        0x02080000L,0x00000000L,0x40080000L,0x42000000L,
        0x00080100L,0x02000100L,0x40000100L,0x00080000L,
        0x00000000L,0x40080000L,0x02080100L,0x40000100L
    },
    {
        0x20000010L,0x20400000L,0x00004000L,0x20404010L,
        0x20400000L,0x00000010L,0x20404010L,0x00400000L,
        0x20004000L,0x00404010L,0x00400000L,0x20000010L,
        0x00400010L,0x20004000L,0x20000000L,0x00004010L,
        0x00000000L,0x00400010L,0x20004010L,0x00004000L,
        0x00404000L,0x20004010L,0x00000010L,0x20400010L,
        0x20400010L,0x00000000L,0x00404010L,0x20404000L,
        0x00004010L,0x00404000L,0x20404000L,0x20000000L,
        0x20004000L,0x00000010L,0x20400010L,0x00404000L,
        0x20404010L,0x00400000L,0x00004010L,0x20000010L,
        0x00400000L,0x20004000L,0x20000000L,0x00004010L,
        0x20000010L,0x20404010L,0x00404000L,0x20400000L,
        0x00404010L,0x20404000L,0x00000000L,0x20400010L,
        0x00000010L,0x00004000L,0x20400000L,0x00404010L,
        0x00004000L,0x00400010L,0x20004010L,0x00000000L,
        0x20404000L,0x20000000L,0x00400010L,0x20004010L
    },
    {
        0x00200000L,0x04200002L,0x04000802L,0x00000000L,
        0x00000800L,0x04000802L,0x00200802L,0x04200800L,
        0x04200802L,0x00200000L,0x00000000L,0x04000002L,
        0x00000002L,0x04000000L,0x04200002L,0x00000802L,
        0x04000800L,0x00200802L,0x00200002L,0x04000800L,
        0x04000002L,0x04200000L,0x04200800L,0x00200002L,
        0x04200000L,0x00000800L,0x00000802L,0x04200802L,
        0x00200800L,0x00000002L,0x04000000L,0x00200800L,
        0x04000000L,0x00200800L,0x00200000L,0x04000802L,
        0x04000802L,0x04200002L,0x04200002L,0x00000002L,
        0x00200002L,0x04000000L,0x04000800L,0x00200000L,
        0x04200800L,0x00000802L,0x00200802L,0x04200800L,
        0x00000802L,0x04000002L,0x04200802L,0x04200000L,
        0x00200800L,0x00000000L,0x00000002L,0x04200802L,
        0x00000000L,0x00200802L,0x04200000L,0x00000800L,
        0x04000002L,0x04000800L,0x00000800L,0x00200002L
    },
    {
        0x10001040L,0x00001000L,0x00040000L,0x10041040L,
        0x10000000L,0x10001040L,0x00000040L,0x10000000L,
        0x00040040L,0x10040000L,0x10041040L,0x00041000L,
        0x10041000L,0x00041040L,0x00001000L,0x00000040L,
        0x10040000L,0x10000040L,0x10001000L,0x00001040L,
        0x00041000L,0x00040040L,0x10040040L,0x10041000L,
        0x00001040L,0x00000000L,0x00000000L,0x10040040L,
        0x10000040L,0x10001000L,0x00041040L,0x00040000L,
        0x00041040L,0x00040000L,0x10041000L,0x00001000L,
        0x00000040L,0x10040040L,0x00001000L,0x00041040L,
        0x10001000L,0x00000040L,0x10000040L,0x10040000L,
        0x10040040L,0x10000000L,0x00040000L,0x10001040L,
        0x00000000L,0x10041040L,0x00040040L,0x10000040L,
        0x10040000L,0x10001000L,0x10001040L,0x00000000L,
        0x10041040L,0x00041000L,0x00041000L,0x00001040L,
        0x00001040L,0x00040040L,0x10000000L,0x10041000L
    }
};


/* Primitive function F.
 * Input is r, subkey array in keys, output is XORed into l.
 * Each round consumes eight 6-bit subkeys, one for
 * each of the 8 S-boxes, 2 longs for each round.
 * Each long contains four 6-bit subkeys, each taking up a byte.
 * The first long contains, from high to low end, the subkeys for
 * S-boxes 1, 3, 5 & 7; the second contains the subkeys for S-boxes
 * 2, 4, 6 & 8 (using the origin-1 S-box numbering in the standard,
 * not the origin-0 numbering used elsewhere in this code)
 * See comments elsewhere about the pre-rotated values of r and Spbox.
 */
#define	F(l,r,key){\
	work = ((r >> 4) | (r << 28)) ^ key[0];\
	l ^= Spbox[6][(int) (work & 0x3f)];\
	l ^= Spbox[4][(int) ((work >> 8) & 0x3f)];\
	l ^= Spbox[2][(int) ((work >> 16) & 0x3f)];\
	l ^= Spbox[0][(int) ((work >> 24) & 0x3f)];\
	work = r ^ key[1];\
	l ^= Spbox[7][(int) (work & 0x3f)];\
	l ^= Spbox[5][(int) ((work >> 8) & 0x3f)];\
	l ^= Spbox[3][(int) ((work >> 16) & 0x3f)];\
	l ^= Spbox[1][(int) ((work >> 24) & 0x3f)];\
}

/* Encrypt or decrypt a block of data in ECB mode */
void des3(DES3_KS ks, bits8_t *block, bits8_t *out)
{
    bits32_t left,right,work;

    /* Read input block and place in left/right in big-endian order */
    left = ((bits32_t)block[0] << 24)
           | ((bits32_t)block[1] << 16)
           | ((bits32_t)block[2] << 8)
           | (bits32_t)block[3];
    right = ((bits32_t)block[4] << 24)
            | ((bits32_t)block[5] << 16)
            | ((bits32_t)block[6] << 8)
            | (bits32_t)block[7];

    /* Hoey's clever initial permutation algorithm, from Outerbridge
     * (see Schneier p 478)
     *
     * The convention here is the same as Outerbridge: rotate each
     * register left by 1 bit, i.e., so that "left" contains permuted
     * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32
     * (using origin-1 numbering as in the FIPS). This allows us to avoid
     * one of the two rotates that would otherwise be required in each of
     * the 16 rounds.
     */
    work = ((left >> 4) ^ right) & 0x0f0f0f0fL;
    right ^= work;
    left ^= work << 4;
    work = ((left >> 16) ^ right) & 0xffff;
    right ^= work;
    left ^= work << 16;
    work = ((right >> 2) ^ left) & 0x33333333L;
    left ^= work;
    right ^= (work << 2);
    work = ((right >> 8) ^ left) & 0xff00ffL;
    left ^= work;
    right ^= (work << 8);
    right = (right << 1) | (right >> 31);
    work = (left ^ right) & 0xaaaaaaaaL;
    left ^= work;
    right ^= work;
    left = (left << 1) | (left >> 31);

    /* First key */
    F(left,right,ks[0]);
    F(right,left,ks[1]);
    F(left,right,ks[2]);
    F(right,left,ks[3]);
    F(left,right,ks[4]);
    F(right,left,ks[5]);
    F(left,right,ks[6]);
    F(right,left,ks[7]);
    F(left,right,ks[8]);
    F(right,left,ks[9]);
    F(left,right,ks[10]);
    F(right,left,ks[11]);
    F(left,right,ks[12]);
    F(right,left,ks[13]);
    F(left,right,ks[14]);
    F(right,left,ks[15]);

    /* Second key (must be created in opposite mode to first key) */
    F(right,left,ks[16]);
    F(left,right,ks[17]);
    F(right,left,ks[18]);
    F(left,right,ks[19]);
    F(right,left,ks[20]);
    F(left,right,ks[21]);
    F(right,left,ks[22]);
    F(left,right,ks[23]);
    F(right,left,ks[24]);
    F(left,right,ks[25]);
    F(right,left,ks[26]);
    F(left,right,ks[27]);
    F(right,left,ks[28]);
    F(left,right,ks[29]);
    F(right,left,ks[30]);
    F(left,right,ks[31]);

    /* First key again */
    F(left,right,ks[32]);
    F(right,left,ks[33]);
    F(left,right,ks[34]);
    F(right,left,ks[35]);
    F(left,right,ks[36]);
    F(right,left,ks[37]);
    F(left,right,ks[38]);
    F(right,left,ks[39]);
    F(left,right,ks[40]);
    F(right,left,ks[41]);
    F(left,right,ks[42]);
    F(right,left,ks[43]);
    F(left,right,ks[44]);
    F(right,left,ks[45]);
    F(left,right,ks[46]);
    F(right,left,ks[47]);

    /* Inverse permutation, also from Hoey via Outerbridge and Schneier */
    right = (right << 31) | (right >> 1);
    work = (left ^ right) & 0xaaaaaaaaL;
    left ^= work;

    right ^= work;
    left = (left >> 1) | (left  << 31);
    work = ((left >> 8) ^ right) & 0xff00ffL;
    right ^= work;
    left ^= work << 8;
    work = ((left >> 2) ^ right) & 0x33333333L;
    right ^= work;
    left ^= work << 2;
    work = ((right >> 16) ^ left) & 0xffffL;
    left ^= work;
    right ^= work << 16;
    work = ((right >> 4) ^ left) & 0x0f0f0f0fL;
    left ^= work;
    right ^= work << 4;

    /* Put the block back into the user's buffer with final swap */
    out[0] = (bits8_t) (right >> 24);
    out[1] = (bits8_t) (right >> 16);
    out[2] = (bits8_t) (right >> 8);
    out[3] = (bits8_t) (right);
    out[4] = (bits8_t) (left >> 24);
    out[5] = (bits8_t) (left >> 16);
    out[6] = (bits8_t) (left >> 8);
    out[7] = (bits8_t) (left);
}

void des3ecb_block_encrypt (DES3_CTX *ctx, bits8_t *in, bits8_t *out)
{
    des3 (ctx->keysched, in, out);
}

void des3cbc_block_encrypt (DES3_CTX *ctx, bits8_t *in, bits8_t *out)
{
    int i = 8;
    bits8_t *cp = ctx->iv;
    bits8_t *inp = in;
    while (i--)
        *cp++ ^= *inp++;

    des3 (ctx->keysched, ctx->iv, out);
    memcpy(ctx->iv, out, 8);
}

void des3cbc_block_decrypt (DES3_CTX *ctx, bits8_t *in, bits8_t *out)
{
    int i = 8;

    bits8_t *cp = ctx->iv;      /* == C[i-1] from last iteration, or IV */
    bits8_t *outp = out;

    des3 (ctx->keysched, in, out);

    while (i--)
        *outp++ ^= *cp++;   /* P[i] = D(C[i]) XOR C[i-1]    */
    memcpy(ctx->iv, in, 8);    /* save C[i] for next iteration */
}

void des3cbc_init_context (void *ctx, bits8_t *key, int decrypt)
{
    des3key ( ((DES3_CTX *) ctx)->keysched, key, decrypt);
    ((DES3_CTX *) ctx)->decrypt = decrypt;
}

void des3cbc_iv_context (void *ctx, bits8_t *iv)
{
    memcpy( ((DES3_CTX *)ctx)->iv, iv, 8);
}

void des3cbc_crypt (void *ctx, bits8_t *in, bits8_t *out, bits32_t n)
{
    int decrypt = ((DES3_CTX *) ctx)->decrypt;

    while (n >= 8)
    {
        if (decrypt)
            des3cbc_block_decrypt ((DES3_CTX *) ctx, in, out);
        else
            des3cbc_block_encrypt ((DES3_CTX *) ctx, in, out);

        in += 8;
        out += 8;
        n -= 8;
    }
}

void des3ecb_init_context (void *ctx, bits8_t *key, int decrypt)
{
    des3cbc_init_context(ctx, key, decrypt);
}

void des3ecb_crypt (void *ctx, bits8_t *in, bits8_t *out, bits32_t n)
{
    while (n >= 8)
    {
        des3ecb_block_encrypt((DES3_CTX *) ctx, in, out);

        in += 8;
        out += 8;
        n -= 8;
    }
}

bits32_t des3cbc_crypt_pkcs7(void *ctx, bits8_t *in, bits32_t in_len, bits8_t *out, bits32_t out_len)
{
    DES3_CTX *_ctx = (DES3_CTX *)ctx;

    if (_ctx->decrypt)
    {
        bits8_t pad;

        // check in_len is align with 8 bytes
        if ((in_len & 7) != 0 || out_len < in_len)
            return 0;

        // decrypt first
        des3cbc_crypt(ctx, in, out, in_len);

        // check padding
        pad = out[in_len - 1];
        in_len -= pad;

        return in_len;
    }
    else
    {
        bits8_t pad[8];
        bits32_t pad_len;

        if (out_len < DES3_OUT_LEN(in_len))
            return 0;

        // calculate pad len
        pad_len = DES3_OUT_LEN(in_len) - in_len;
        in_len = DES3_IN_LEN(in_len);

        // set unencrypted bytes to pad
        memcpy(pad, in + in_len, 8 - pad_len);

        // set left bytes to pad_len
        memset(pad + 8 - pad_len, pad_len, pad_len);

        // do crypt
        des3cbc_crypt(ctx, in, out, in_len);

        // crypt pad
        des3cbc_crypt(ctx, pad, out + in_len, 8);

        return in_len + 8;
    }
}


bits32_t des3ecb_crypt_pkcs7(void *ctx, bits8_t *in, bits32_t in_len, bits8_t *out, bits32_t out_len)
{
    DES3_CTX *_ctx = (DES3_CTX *)ctx;

    if (_ctx->decrypt)
    {
        bits8_t pad;

        // check in_len is align with 8 bytes
        if ((in_len & 7) != 0 || out_len < in_len)
            return 0;

        // decrypt first
        des3ecb_crypt(ctx, in, out, in_len);

        // check padding
        pad = out[in_len - 1];
        in_len -= pad;

        return in_len;
    }
    else
    {
        bits8_t pad[8];
        bits32_t pad_len;

        if (out_len < DES3_OUT_LEN(in_len))
            return 0;

        // calculate pad len
        pad_len = DES3_OUT_LEN(in_len) - in_len;
        in_len = DES3_IN_LEN(in_len);

        // set unencrypted bytes to pad
        memcpy(pad, in + in_len, 8 - pad_len);

        // set left bytes to pad_len
        memset(pad + 8 - pad_len, pad_len, pad_len);

        // do crypt
        des3ecb_crypt(ctx, in, out, in_len);

        // crypt pad
        des3ecb_crypt(ctx, pad, out + in_len, 8);

        return in_len + 8;
    }
}
